Scientific Python antipatterns advent calendar day three

For today, a follow up to yesterday’s post. As a reminder, I’ll post one tiny example per day with the intention that they should only take a couple of minutes to read.

If you want to read them all but can’t be bothered checking this website each day, sign up for the mailing list:

Sign up for the mailing list

and I’ll send a single email at the end with links to them all.

Not using enumerate

Yesterday we looked at the core problem of iterating over a list, and I presented the canonical Python version:

fruits = ['apple', 'banana', 'grapefruit', 'strawberry']

for fruit in fruits:
    print(fruit)
apple
banana
grapefruit
strawberry

Sometimes we need access to the index inside the loop. Solutions that I’ve commonly seen involve using range:

for index in range(len(fruits)):
    print(index, fruits[index])
0 apple
1 banana
2 grapefruit
3 strawberry

or managing a separate index variable:

index = 0

for fruit in fruits:
    print(index, fruit)
    index = index + 1
0 apple
1 banana
2 grapefruit
3 strawberry

The Pythonic way to handle this is with a built in function that is designed for exactly this purpose - enumerate. Look what happens when we call enumerate with our input list:

# enumerate is lazy, so turn the result into a list to show it
list(enumerate(fruits))
[(0, 'apple'), (1, 'banana'), (2, 'grapefruit'), (3, 'strawberry')]

Each element in the list turns into a tuple with the index attached. So we can rewrite our loop like this:

for tup in enumerate(fruits):
    print(tup[0], tup[1])
0 apple
1 banana
2 grapefruit
3 strawberry

It’s probably a good idea to assign variable names to these tuple elements to make the code more readable:

for tup in enumerate(fruits):
    index = tup[0]
    fruit = tup[1]
    print(index, fruit)
0 apple
1 banana
2 grapefruit
3 strawberry

but an even nicer way to handle this in Python is to do the assignment on the for line:

for index, fruit in enumerate(fruits):
    print(index, fruit)
0 apple
1 banana
2 grapefruit
3 strawberry

Concise, clear and readable.

Bonus: sometimes for human consumption we want to have the index start from some other number (mostly likely one). We can adjust the index in the loop easily enough:

for index, fruit in enumerate(fruits):
    print(f'the fruit at position {index + 1} is {fruit}')
the fruit at position 1 is apple
the fruit at position 2 is banana
the fruit at position 3 is grapefruit
the fruit at position 4 is strawberry

but enumerate also has a start argument that will handle this logic:

for index, fruit in enumerate(fruits, start=1):
    print(f'the fruit at position {index} is {fruit}')
the fruit at position 1 is apple
the fruit at position 2 is banana
the fruit at position 3 is grapefruit
the fruit at position 4 is strawberry

One more time; if you want to see the rest of these little write-ups, sign up for the mailing list:

Sign up for the mailing list